package jmine.tec.aom.services.include;

import jmine.tec.aom.AOMMessages;
import jmine.tec.aom.domain.AbstractPropertyValue;
import jmine.tec.aom.domain.Propertized;
import jmine.tec.aom.domain.Property;
import jmine.tec.aom.exception.FillException;
import jmine.tec.aom.export.PropertyValueAssociation;
import jmine.tec.aom.util.PropertyValueUtils;
import jmine.tec.di.annotation.Injected;
import jmine.tec.services.annotations.Execution;
import jmine.tec.services.annotations.Input;
import jmine.tec.services.annotations.ServiceImplementor;
import jmine.tec.services.api.ActionsEnum;
import jmine.tec.services.api.RawInput;
import jmine.tec.services.api.ServiceValidationExecutionException;
import jmine.tec.services.api.export.ReferenceMap;
import jmine.tec.services.api.export.ServiceFiller;
import jmine.tec.services.export.impl.ExportUtils;
import jmine.tec.services.io.ServiceBean;
import bancosys.tec.persist.Persistable;

/**
 * Creates and adds a new {@link AbstractPropertyValue} to a {@link Propertized}.
 * 
 * @author jrenaut
 */
@ServiceImplementor(action = ActionsEnum.INCLUIR)
public class PropertyValueService implements ServiceFiller<PropertyValueAssociation> {

    public static final String PROPERTIZED = "Propertized";

    public static final String PROPERTY = "Property";

    public static final String VALUE = "Value";

    public static final String LOCKED = "Locked";

    public static final String DATABASE_ID = "Database ID";

    private Long databaseId;

    private PropertyValueUtils propertyUtils;

    /**
     * @param propertizedEntity an object that must implement {@link Persistable} and {@link Propertized}
     * @param value the value
     * @param locked true if the value should be marked as locked
     * @param serviceProperties the {@link RawInput}
     * @throws FillException if the value cannot be converted from {@link String} to the actual {@link Property} type
     */
    @Execution
    @SuppressWarnings("deprecation")
    public void execute(@Input(fieldName = PROPERTIZED) Object propertizedEntity, @Input(fieldName = VALUE) String value,
            @Input(fieldName = LOCKED, defaultValue = "false") boolean locked, @Injected RawInput serviceProperties) throws FillException {

        if (!(propertizedEntity instanceof Persistable) || !(propertizedEntity instanceof Propertized)) {
            throw new ServiceValidationExecutionException(AOMMessages.MUST_BE_PROPERTIZED_AND_PERSISTABLE.create(PROPERTIZED));
        }

        Propertized propertized = (Propertized) propertizedEntity;
        Persistable persistable = (Persistable) propertizedEntity;

        String propertyName = serviceProperties.getRaw(PROPERTY);
        if (propertyName == null) {
            throw new ServiceValidationExecutionException(AOMMessages.FIELD_REQUIRED.create(PROPERTY));
        }

        Property property = this.findProperty(propertized, propertyName);
        if (property == null) {
            throw new ServiceValidationExecutionException(AOMMessages.PROPERTY_NOT_FOUND.create(propertyName));
        }

        AbstractPropertyValue propValue = propertized.getPropertyValue(property);
        if (propValue != null) {
            propValue.setValueAsString(value);
        } else {
            propValue = this.propertyUtils.createPropertyValue(property, value);
            if (this.databaseId != null) {
                propValue.getController().getSessionFactory().getCurrentSession().save(propValue, this.databaseId);
            }
            propertized.addPropertyValue(propValue);
        }

        propValue.setLocked(locked);

        persistable.getPersister().save();
    }

    /**
     * Procura a caracteristica
     * 
     * @param entidade a entidade que vai receber o valor da caracteristica
     * @param nomeCaracteristica o nome da caracteristica
     * @return a caracteristica
     */
    protected Property findProperty(Propertized entidade, String nomeCaracteristica) {
        return entidade.getPropertyByName(nomeCaracteristica);
    }

    /**
     * {@inheritDoc}
     */
    public void fillServiceBean(ServiceBean bean, ReferenceMap referenceMap, PropertyValueAssociation entity) {
        ExportUtils.setService(bean, PropertyValueService.class);
        ExportUtils.putDatabaseId(bean, DATABASE_ID, entity.getAssociatedEntity());
        ExportUtils.put(bean, PROPERTIZED, referenceMap.getReference(entity.getBaseEntity()));
        ExportUtils.put(bean, LOCKED, entity.getAssociatedEntity().isLocked());
        if (entity.getAssociatedEntity().getProperty() != null) {
            ExportUtils.put(bean, PROPERTY, entity.getAssociatedEntity().getProperty().getName());
        }
        ExportUtils.put(bean, VALUE, entity.getAssociatedEntity().getValueAsString());
    }

    /**
     * @param databaseId the databaseId to set
     */
    @Input(fieldName = DATABASE_ID, required = false)
    public void setDatabaseId(Long databaseId) {
        this.databaseId = databaseId;
    }

    /**
     * @param propertyUtils the propertyUtils to set
     */
    @Injected
    public void setPropertyUtils(PropertyValueUtils propertyUtils) {
        this.propertyUtils = propertyUtils;
    }

}
